package com.moons.uvcdemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.Size;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;

import com.xqd.sdk.libaicamera.AICamera;
import com.xqd.sdk.libaicamera.AICameraManager;
import com.xqd.sdk.libaicamera.FeatureComparator;
import com.xqd.sdk.libaicamera.face.FaceAttribute;
import com.xqd.sdk.libaicamera.face.FaceObject;
import com.xqd.sdk.libaicamera.utils.Utils;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class RgbActivity extends AppCompatActivity implements View.OnClickListener{
    private static final String TAG = "MainActivity";
    private static final int MSG_AICAMERA_ADDED = 0;
    private static final int MSG_AICAMERA_REMOVED = 1;

    private TextView mTvVersion;
    private TextView mTvNumOfCameras;
    private Button mBtnOpenAICamera;
    private Button mBtnCloseAICamera;
    private Spinner mSpCameras;

    private AICameraManager mAICameraManager;
    private SpinnerAdapter mAdapter;

    private List<FaceDbItem> mfaceDbItems;

    private PreviewWidget mPreviewWidget;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rgb);
        checkPermissions();
        mAICameraManager = AICameraManager.instance();
        mAICameraManager.setDeviceEventListener(mDeviceEventListener);
        mAICameraManager.start();
        initViews();
        mfaceDbItems = new ArrayList<>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                loadFaceDbItems();
            }
        }).start();
    }

    @Override
    protected void onPause() {
        Log.d(TAG, "onPause");
        super.onPause();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            Log.d(TAG, "onWindowFocusChanged ==> hideSystemBar");
            hideSystemBar();
        }
    }

    @Override
    protected void onResume() {
        Log.d(TAG, "onResume");
        super.onResume();
    }

    @Override
    public void onClick(View view) {
        if (view == mBtnOpenAICamera) {
            int pos = mSpCameras.getSelectedItemPosition();

            AICamera aiCamera = (AICamera)mSpCameras.getSelectedItem();
            if (aiCamera!=null) {
                if (aiCamera.open()) {
                    Size size = aiCamera.getPreviewSize();
                    if (size != null) {
                        Log.d(TAG, "current preview size:width ==> " + size.getWidth() + ", height ==> " + size.getHeight());
                    }
                    //aiCamera.setPreviewSize(640, 480);
                    List<Size> previewSizes = aiCamera.getSupportPreviewSizes();
                    if (previewSizes.size() > 0) {
                        for(Size sz:previewSizes) {
                            Log.d(TAG, String.format("%d*%d", sz.getWidth(), sz.getHeight()));
                            if (is720P(sz)) {
                                aiCamera.setPreviewSize(sz);
                            }
                        }
                    }
                    aiCamera.setCaptureCallback(mCaptureCallback);
                    aiCamera.startCapture();
                    mBtnOpenAICamera.setEnabled(false);
                    mBtnCloseAICamera.setEnabled(true);
                    mSpCameras.setSelection(pos);
                    mSpCameras.setEnabled(false);
                } else {
                    Log.d(TAG, "could not open AICamera");
                }
            }
        } else if (view == mBtnCloseAICamera) {
            AICamera aiCamera = (AICamera)mSpCameras.getSelectedItem();
            if (aiCamera!=null) {
                aiCamera.stopCapture();
                aiCamera.close();
                mBtnOpenAICamera.setEnabled(true);
                mBtnCloseAICamera.setEnabled(false);
                mSpCameras.setEnabled(true);
            }
        }
    }

    private void hideSystemBar() {
        if (Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }

    private boolean is720P(Size size) {
        return ((size.getWidth()==1280)&&(size.getHeight()==720)||((size.getWidth()==720)&&(size.getHeight()==1280)));
    }

    private void checkPermissions(){
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
        }
    }

    private List<float[]> getFromFaceDbItems() {
        List<float[]> featuresList = new ArrayList<>();
        for (FaceDbItem faceDbItem : mfaceDbItems) {
            featuresList.add(faceDbItem.features());
        }
        return featuresList;
    }

    private void loadFaceDbItems() {
        try {
            AssetManager assetManager = getApplicationContext().getAssets();
            InputStream is = assetManager.open("facedb_st.fdb");
            DataInputStream dis = new DataInputStream(is);
            int nItems = is.available()/FaceDbItem.itemSize();
            for (int i =0; i<nItems; i++) {
                int id = Integer.reverseBytes(dis.readInt());
                int vectorLength = Integer.reverseBytes(dis.readInt());
                float[] features = new float[vectorLength];
                for (int j = 0; j < vectorLength; j++) {
                    int temp = Integer.reverseBytes(dis.readInt());
                    features[j] = Float.intBitsToFloat(temp);
                }
                dis.skipBytes(Float.BYTES * (512 - vectorLength));

                byte[] buffer = new byte[64];
                dis.read(buffer, 0, 64);
                String name = new String(buffer, "utf-8").trim();
                Log.d(TAG, "index " + i + " : id ==>" + id + " , vector length ==> " + vectorLength + ", name ==> " + name);
                FaceDbItem faceDbItem = new FaceDbItem(id, features, vectorLength, name);
                mfaceDbItems.add(faceDbItem);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == MSG_AICAMERA_ADDED) {
                int numOfCameras = mAICameraManager.numOfCameras();
                mTvNumOfCameras.setText(String.format(getString(R.string.current_camera_num),numOfCameras));
                AICamera tAICamera = new AICamera("/dev/video1");
                List<AICamera> deviceList = mAICameraManager.getDeviceList();;
                deviceList.add(tAICamera);
                if (deviceList != null) {
                    Log.d(TAG, "deice list not null, size = " + deviceList.size());
                    mAdapter.updateDeviceList(deviceList);
                }
                if (numOfCameras > 0) {
                    mBtnOpenAICamera.setEnabled(true);
                    mSpCameras.setSelection(0);
                }
            } else if (msg.what == MSG_AICAMERA_REMOVED) {
                int numOfCameras = mAICameraManager.numOfCameras();
                mTvNumOfCameras.setText(String.format(getString(R.string.current_camera_num),numOfCameras));
                List<AICamera> deviceList = mAICameraManager.getDeviceList();
                if (deviceList != null) {
                    Log.d(TAG, "deice list not null, size = " + deviceList.size());
                    mAdapter.updateDeviceList(deviceList);
                }
                if (numOfCameras == 0) {
                    mBtnOpenAICamera.setEnabled(false);
                    mBtnCloseAICamera.setEnabled(false);
                    mSpCameras.setEnabled(true);
                }
            }
        }
    };

    private AICamera.CaptureCallback mCaptureCallback = new AICamera.CaptureCallback() {
        @Override
        public void onFrameData(byte[] data, AICamera camera) {
            Log.d(TAG, "onFrameData size = " + data.length + " from " + camera.getPath());
            mPreviewWidget.putFrameData(data);
            //Log.d(TAG, "frame id ==>" + Utils.getIdFromFrameData(data));
        }

        @Override
        public void onFaceData(byte[] data, AICamera camera) {
            Log.d(TAG, "onFaceData");
            List<FaceObject> faceObjects = camera.getFaceObjectParser().getFaceObjectList(data, 0);
            Log.d(TAG, "get " +  faceObjects.size() + " faces");
            FaceObject faceObject = faceObjects.get(0);
            if (faceObject.hasFeatures()) {
                // TODO: do you face recognize here

            }else {
                if (!mPreviewWidget.isFrameSizeAvailable()) {
                    Size size = Utils.getSizeFromFaceData(data);
                    Log.d(TAG, size.toString());
                    mPreviewWidget.setFrameSize(size);
                }
                mPreviewWidget.putFaceObject(faceObjects);
            }
        }
    };

    private void initViews() {
        mPreviewWidget = findViewById(R.id.preview_widget);
        mBtnOpenAICamera = findViewById(R.id.btn_open_aicmara);
        mBtnOpenAICamera.setOnClickListener(this);

        mBtnCloseAICamera = findViewById(R.id.btn_close_aicamera);
        mBtnCloseAICamera.setOnClickListener(this);

        mTvVersion = findViewById(R.id.version);
        mTvVersion.setText(AICamera.getSDKVersion());
        mTvNumOfCameras = findViewById(R.id.textViewCameraNum);

        mAdapter = new SpinnerAdapter(getApplicationContext(), mAICameraManager.getDeviceList());
        mSpCameras = findViewById(R.id.cameraListSpinner);
        mSpCameras.setAdapter(mAdapter);

        mTvNumOfCameras.setText(String.format(getString(R.string.current_camera_num), mAICameraManager.numOfCameras()));
    }

    private AICameraManager.DeviceEventListener mDeviceEventListener = new AICameraManager.DeviceEventListener() {
        @Override
        public void onDeviceAdded(AICamera cameraDevice) {
            Log.d(TAG, "onDeviceAdded");
            mHandler.sendEmptyMessageAtTime(MSG_AICAMERA_ADDED, 100);
        }

        @Override
        public void onDeviceRemoved(AICamera cameraDevice) {
            mHandler.sendEmptyMessageDelayed(MSG_AICAMERA_REMOVED, 100);
        }
    };

    public class SpinnerAdapter extends BaseAdapter {
        private List<AICamera> mDeviceList;
        private Context mContext;
        public SpinnerAdapter(Context context, List<AICamera> deviceList) {
            mDeviceList = deviceList;
            mContext = context;
        }

        public void updateDeviceList(List<AICamera> deviceList) {
            mDeviceList = deviceList;
        }
        @Override
        public int getCount() {
            return mDeviceList.size();
        }

        @Override
        public Object getItem(int position) {
            return mDeviceList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup viewGroup) {
            ViewHolder holder;
            AICamera aiCamera = mDeviceList.get(position);
            if (convertView==null){
                holder = new ViewHolder();
                TextView textView = new TextView(mContext);
                holder.textView = textView;
                convertView = textView;
                convertView.setTag(holder);

            }else {
                holder = (ViewHolder) convertView.getTag();
            }

            holder.textView.setText(aiCamera.getPath());
            return convertView;
        }

        private class ViewHolder{
            TextView textView;
        }
    }
}
